﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Ben.Http;
using System.Reflection;

namespace W
{
    class Program
    {
        static void Main()
        {
            var server = new HttpService(9999);
            //var fileHandler = new FileBasedRequestHandler(@"C:\");
            //fileHandler.AddContentType(".jpg", "image/jpeg");
            ////server.Handlers.Add(new AssemblyBasedRequestHandler(@"D:\Projects\Server\Web\bin\Debug\root"));
            //server.Handlers.Add(fileHandler); 
            server.Handlers.Add(new ScreenBroadcastRequestHandler());
            server.Start();
            Console.ReadLine();
            server.Stop();
            Thread.Sleep(100);
        }
    }

    

    public class ScreenBroadcastRequestHandler : IRequestHandler
    {
        private readonly List<ClientRequest> _clients;
        private readonly List<object> _sync;
        private bool GetSync(object token)
        {
            lock (_sync)
            {
                return _sync.Contains(token);
            }
        }
        private void SetSync(object token, bool enable)
        {
            lock (_sync)
            {
                var contains = _sync.Contains(token);
                if ((enable && contains) ||(!enable && !contains)) return;
                if (enable)
                    _sync.Add(token);
                else
                    _sync.Remove(token);
            }
        }
        public ScreenBroadcastRequestHandler()
        {
            _clients = new List<ClientRequest>();
            _sync = new List<object>();
        }
        
        public bool CanHandle(ClientRequest request)
        {
            return true;
        }

        public bool IsRunning
        {
            get { return GetSync("IsRunning"); }
            private set { SetSync("IsRunning", value); }
        }
        protected bool KeepRunning
        {
            get { return GetSync("KeepRunning"); }
            set { SetSync("KeepRunning", value); }
        }
        public void Process(ClientRequest request)
        {
            if (request == null) return;
            try
            {
                request.Client.Blocking = true;
                request.Client.Send(_header);
                _clients.Add(request);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        public void Start()
        {
            if (IsRunning) return;
            IsRunning = true;
            new Thread(CaptureScreen) {IsBackground = true}.Start();
        }

        private readonly byte[] _header = Encoding.UTF8.GetBytes(@"HTTP/1.0 200 OK
Content-Type: multipart/x-mixed-replace; boundary=2CC4C358-90C3-4472-9882-6A8B25EF4CA4

--2CC4C358-90C3-4472-9882-6A8B25EF4CA4
");
        private readonly byte[] _imageHeader = Encoding.UTF8.GetBytes(@"Content-Type: image/jpeg

");
        private readonly byte[] _imageFooter = Encoding.UTF8.GetBytes(@"
--2CC4C358-90C3-4472-9882-6A8B25EF4CA4
");
        private readonly byte[] _error = Encoding.UTF8.GetBytes(@"HTTP/1.0 404 Not Found
Content-Length: 0
Content-Type: text/html

");

        private void CaptureScreen()
        {
            KeepRunning = true;
            var bounds = Screen.PrimaryScreen.Bounds;
            var mem = new MemoryStream();
            var bmp = new Bitmap(bounds.Width, bounds.Height);
            var g = Graphics.FromImage(bmp);
            try
            {
                var list = new List<ClientRequest>();
                while (KeepRunning)
                {
                    try
                    {
                        mem.SetLength(0);
                        g.CopyFromScreen(0, 0, 0, 0, bounds.Size);
                        bmp.Save(mem, ImageFormat.Jpeg);
                        var buffer = mem.ToArray();

                        foreach (var i in _clients.ToArray())
                        {
                            try
                            {
                                i.Client.Send(_imageHeader);
                                i.Client.Send(buffer);
                                i.Client.Send(_imageFooter);
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.Message);
                                list.Add(i);
                            }
                        }
                        foreach (var i in list)
                            _clients.Remove(i);
                        list.Clear();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    Thread.Sleep(1000);
                }
            }
            finally
            {
                g.Dispose();
                bmp.Dispose();
                mem.Dispose();
            }
            IsRunning = false;
        }
        public void Stop()
        {
            if (!IsRunning) return;
            KeepRunning = false;
        }
    }

    public class JpegStreamingRequestHandler : IRequestHandler
    {
        
        public string Address { get; private set; }
        public string Username { get; private set; }
        public string Password { get; private set; }
        public bool IsRunning { get; private set; }
        public bool CanHandle(ClientRequest request) { return true; }

        public void Process(ClientRequest request)
        {
            string address = null;
            string username = null;
            string password = null;
            foreach (var i in request.QueryString)
            {
                switch (i.Key.ToLower())
                {
                    case "user": username = i.Value; break;
                    case "pwd": password = i.Value; break;
                    case "ip": address = i.Value; break;
                }
            }
        }

        public void Start()
        {
        }

        public void Stop()
        {
        }
        class SnapshotAgent
        {
            public readonly List<Socket> Clients;
            private int _index;
            private readonly MemoryStream _mem;
            private readonly Socket _socket;
            private readonly List<byte> _tmp;
            private readonly string _host;
            private readonly int _port;
            private readonly string _username;
            private readonly string _password;
            private Action<object> _fp;
            public SnapshotAgent(string host, int port, string username, string password)
            {
                Clients = new List<Socket>();
                _host = host;
                _port = port;
                _username = username;
                _password = password;
                _fp = GetImage;
                _tmp = new List<byte>();
                _mem = new MemoryStream();
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            }
            private static byte[] GetImageBuffer(string host, int port, string username, string password)
            {
                var queryString = string.Format("/cgi-bin/encoder?USER={0}&PWD={1}&SNAPSHOT", username, password);
                return GetBinary(host, port, "GET", queryString);
            }
            private static byte[] GetBinary(string host, int port, string method, string queryString)
            {
                const string pattern = "{0} {1} HTTP/1.1\r\n\r\n";
                var command = string.Format(pattern, method, queryString);
                using (var mem = new MemoryStream())
                {
                    var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    socket.Connect(host, port);
                    socket.Send(Encoding.Default.GetBytes(command));
                    var header = new List<string>();
                    var tmp = new List<byte>();
                    int len;
                    Action<byte> a;
                    Action<byte> d = mem.WriteByte;
                    Action<byte> h =
                        b =>
                            {
                                if (b == '\r') return;
                                if (b == '\n')
                                {
                                    if (tmp.Count < 1)
                                    {
                                        a = d;
                                        return;
                                    }
                                    var line = Encoding.ASCII.GetString(tmp.ToArray());
                                    header.Add(line);
                                    tmp.Clear();
                                    return;
                                }
                                tmp.Add(b);
                            };
                    a = h;
                    var buffer = new byte[1024];
                    do
                    {
                        len = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
                        for (var i = 0; i < len; i++)
                            a(buffer[i]);
                    } while (len > 0);
                    socket.Close();
                    return mem.ToArray();
                }
            }
            
            public void Push() { _fp(0); }

            private byte[] _imageBuffer;

            private void GetImage(object state)
            {
                try
                {
                    _imageBuffer = GetImageBuffer(_host, _port, _username, _password);
                    _fp = SendToClient;
                    _index = 0;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }

            }

            private void SendToClient(object state)
            {
                
            }

            private void Connect(object state)
            {
                const string pattern = "{0} /cgi-bin/encoder?USER={1}&PWD={2}&SNAPSHOT HTTP/1.1\r\n\r\n";
                try
                {
                    var command = Encoding.ASCII.GetBytes(string.Format(pattern, "GET", _username, _password));
                    _socket.Connect(_host, _port);
                    _socket.Send(command);
                    _tmp.Clear();
                    _mem.SetLength(0);
                    _fp = ReceiveHeader;
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Connect failed: "+ ex.Message);
                }
            }

            private void ReceiveHeader(object state)
            {
            }
            private void ReceiveBody(object state)
            {

            }
            private void RaiseEvent(object state)
            {
                
            }
            private void Disconnect(object state)
            {
                
            }

        }

    }
    public class FileBasedRequestHandler : IRequestHandler
    {
        private readonly Dictionary<string, string> _contentTypes;
        public string RootDirectory { get; private set; }
        public bool IsRunning { get { return true; } }
        public FileBasedRequestHandler(string rootDirectory)
        {
            if (!Directory.Exists(rootDirectory)) throw new DirectoryNotFoundException();
            RootDirectory = rootDirectory;
            _contentTypes = new Dictionary<string, string>();
        }
        public bool CanHandle(ClientRequest request) { return true; }
        public void Start() { }
        public void Stop() { } 
        public void AddContentType(string extName, string contentType)
        {
            _contentTypes[extName.ToLower()] = contentType;
        }
        public void RemoveContentType(string extName)
        {
            _contentTypes.Remove(extName.ToLower());
        }
        private string GetContentType(string path, string defaultContentType)
        {
            if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path");
            var ext = Path.GetExtension(path);
            if (string.IsNullOrEmpty(ext)) return defaultContentType;
            ext = ext.ToLower();
            return (!_contentTypes.ContainsKey(ext)) ? defaultContentType : _contentTypes[ext];

        }
        public void Process(ClientRequest request)
        {
            if (request == null || request.Method == null) return;
            try
            {
                Console.WriteLine(request.RawUrl);
                var file = (RootDirectory + request.Path.Replace('/', '\\')).Replace("\\\\", "\\");
                if (!File.Exists(file))
                {
                    request.ResponseStatus(HttpStatusCodes.ClientErrorNotFound);
                    return;
                }
                var buffer = File.ReadAllBytes(file);
                var header = Encoding.Default.GetBytes("HTTP/1.0 200 OK\r\nContent-Length: {Content-Length}\r\nContent-Type: {Content-Type}\r\n\r\n"
                    .Replace("{Content-Length}", buffer.Length.ToString())
                    .Replace("{Content-Type}", GetContentType(request.Path, "text/html")));
                request.Client.Send(header);
                request.Client.Send(buffer);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
    public class DictionaryBasedRequestHandler : Dictionary<string, IRequestHandler>, IRequestHandler
    {
        public bool CaseSensitive { get; set; }
        public bool IsRunning { get; private set; }

        private bool TryGetHandler(ClientRequest request, out IRequestHandler handler)
        {
            if (CaseSensitive) return TryGetValue(request.Path, out handler);
            var path = request.Path.ToLower();
            foreach (var i in this)
            {
                if (i.Key.ToLower() != path) continue;
                handler = i.Value;
                return true;
            }
            handler = null;
            return false;
        }
        public bool CanHandle(ClientRequest request)
        {
            IRequestHandler handler;
            return TryGetHandler(request, out handler);
        }
        public void Process(ClientRequest request)
        {
            IRequestHandler handler;
            if (!TryGetHandler(request, out handler))
            {
                request.ResponseStatus(HttpStatusCodes.ClientErrorNotFound);
                return;
            }
            handler.Process(request);
        }
        public void Start()
        {
            foreach (var i in this)
            {
                try
                {
                    i.Value.Start();
                }
                catch
                {
                }
            }
            IsRunning = true;
        }
        public void Stop()
        {
            foreach (var i in this)
            {
                try
                {
                    i.Value.Stop();
                }
                catch
                {
                }
            }
        }
    }
    public class AssemblyBasedRequestHandler : IRequestHandler
    {
        private readonly SortedDictionary<string, IRequestHandler> _handlers;
        public string RootPath { get; private set; }


        public AssemblyBasedRequestHandler(string rootPath)
        {
            if (!Directory.Exists(rootPath)) 
                throw new DirectoryNotFoundException(rootPath);
            RootPath = rootPath;
            _handlers = new SortedDictionary<string, IRequestHandler>();
        }

        private static void PrepareHandlers(string path, IDictionary<string, IRequestHandler> handlers)
        {
            foreach (var s in Directory.GetFiles(path))
            {
                var fn = Path.Combine(path, s);
                try
                {
                    var asm = Assembly.LoadFrom(fn);
                    if (asm == null) continue;
                    foreach (var type in asm.GetTypes())
                    {
                        try
                        {
                            var instance = Activator.CreateInstance(type) as IRequestHandler;
                            if (instance == null) continue;
                            handlers[fn.Replace('\\', '/')] = instance;
                            instance.Start();
                            break;
                        }
                        catch { continue; }
                    }
                }
                catch { continue; }
            }
            foreach (var dir in Directory.GetDirectories(path))
            {
                PrepareHandlers(dir, handlers);
            }
        }

        public bool CanHandle(ClientRequest request)
        {
            return true;
        }

        public bool IsRunning { get { return _handlers.Count > 0; } }

        public void Process(ClientRequest request)
        {
            foreach (var i in _handlers)
            {
                
                if (!i.Key.EndsWith(request.Path)) continue;
                i.Value.Process(request);
                return;
            }
            request.ResponseStatus(HttpStatusCodes.ClientErrorNotFound);
        }

        public void Start()
        {
            _handlers.Clear();
            PrepareHandlers(RootPath, _handlers);
        }

        public void Stop()
        {
            _handlers.Clear();
        }
    }
    public class HttpService
    {
        private readonly TcpServer _tcp;
        public readonly List<IRequestHandler> Handlers;
        public HttpService(int port)
        {
            Handlers = new List<IRequestHandler>();
            _tcp = new TcpServer(port);
            _tcp.ClientAccepted += ClientAccepted;
        }

        private void ClientAccepted(object sender, SocketClientEventArgs e)
        {
            var request = new ClientRequest(e.Client);
            foreach (var handler in Handlers)
            {
                if (!handler.CanHandle(request)) continue;
                handler.Process(request);
                return;
            }
            request.ResponseStatus(HttpStatusCodes.ClientErrorNotFound);
        }

        public void Start()
        {
            _tcp.Start();
            foreach (var handler in Handlers)
            {
                handler.Start();
            }
        }

        public void Stop()
        {
            _tcp.Stop();
            foreach (var handler in Handlers)
            {
                handler.Stop();
            }
        }
    }
    public class ImageClock2
    {
        private readonly byte[] _header = Encoding.UTF8.GetBytes(@"HTTP/1.0 200 OK
Content-Type: multipart/x-mixed-replace; boundary=2CC4C358-90C3-4472-9882-6A8B25EF4CA4

--2CC4C358-90C3-4472-9882-6A8B25EF4CA4
");
        private readonly byte[] _imageHeader = Encoding.UTF8.GetBytes(@"Content-Type: image/jpeg

");
        private readonly byte[] _imageFooter = Encoding.UTF8.GetBytes(@"
--2CC4C358-90C3-4472-9882-6A8B25EF4CA4
");
        private readonly byte[] _error = Encoding.UTF8.GetBytes(@"HTTP/1.0 404 Not Found
Content-Length: 0
Content-Type: text/html

");
        private readonly List<Socket> _clients;
        private List<object> _keepRunning;
        private readonly object _syncRoot = new object();
        public ImageClock2()
        {
            _clients = new List<Socket>();
        }
        public void Add(Socket client)
        {
            Console.WriteLine("{0} connected.", client.RemoteEndPoint);
            var r = new ClientRequest(client);
            if (r.RawUrl != null && r.RawUrl.ToLower() == "/a.jpg")
            {
                client.Blocking = true;
                client.Send(_header);
                lock (_syncRoot)
                {
                    _clients.Add(client);
                }
                return;
            }
            client.Send(_error);
            client.Shutdown(SocketShutdown.Both);
            client.Close(1000);
            throw new Exception("invalid request!");
        }
        public virtual void Start()
        {
            _keepRunning = new List<object> { "1" };
            new Thread(Execute) { IsBackground = true }.Start();
        }

        public void Execute()
        {
            const double fps = 30.0;
            var rect = new Rectangle(0, 0, 140, 30);
            var bmp = new Bitmap(140, 30);
            var font = new Font("Arial", 16);
            var brush = new SolidBrush(Color.Yellow);
            var g = Graphics.FromImage(bmp);
            var mem = new MemoryStream();
            try
            {
                while (_keepRunning.Count > 0)
                {
                    var nextTime = DateTime.Now.AddMilliseconds(1000.0/fps);
                    g.Clear(Color.Black);
                    g.DrawString(DateTime.Now.ToString("HH:mm:ss.fff"), font, brush, rect);
                    mem.SetLength(0);
                    bmp.Save(mem, ImageFormat.Jpeg);
                    var buffer = mem.ToArray();
                    lock (_syncRoot)
                    {
                        foreach (var c in _clients.ToArray())
                        {
                            try
                            {
                                c.Send(_imageHeader);
                                c.Send(buffer);
                                c.Send(_imageFooter);
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.Message);
                                _clients.Remove(c);
                            }
                        }
                    }
                    var sleep = nextTime.Subtract(DateTime.Now);
                    if (sleep <= TimeSpan.Zero) continue;
                    Thread.Sleep(sleep);
                }
            }
            finally
            {
                brush.Dispose();
                font.Dispose();
                mem.Dispose();
                g.Dispose();
                bmp.Dispose();
            }
        }
        public void Stop()
        {
            _keepRunning.Clear();
        }
    }
    public class ImmutableStack<T>
    {
        private readonly T _value;
        private readonly ImmutableStack<T> _next;
        private readonly bool _isEmpty;
        public ImmutableStack() { _isEmpty = true; }
        internal ImmutableStack(T value, ImmutableStack<T> next)
        {
            _value = value;
            _next = next;
            _isEmpty = false;
        }
        public ImmutableStack<T> Push(T value)
        {
            return new ImmutableStack<T>(value, this);
        }
        public ImmutableStack<T> Pop(out T value)
        {
            if (_isEmpty) throw new Exception("empty.");
            value = _value;
            return _next;
        }
    }
//    public class ImageClockServer : ImageClock
//    {
//        private readonly byte[] _header = Encoding.UTF8.GetBytes(@"HTTP/1.0 200 OK
//Content-Type: multipart/x-mixed-replace; boundary=2CC4C358-90C3-4472-9882-6A8B25EF4CA4
//
//--2CC4C358-90C3-4472-9882-6A8B25EF4CA4
//");
//        private readonly byte[] _imageHeader = Encoding.UTF8.GetBytes(@"Content-Type: image/jpeg
//
//");
//        private readonly byte[] _imageFooter = Encoding.UTF8.GetBytes(@"
//--2CC4C358-90C3-4472-9882-6A8B25EF4CA4
//");
//        private readonly byte[] _error = Encoding.UTF8.GetBytes(@"HTTP/1.0 404 Not Found
//Content-Length: 0
//Content-Type: text/html
//
//");
//        private readonly List<Socket> _clients;

//        public ImageClockServer()
//        {
//            _clients = new List<Socket>();
//        }
//        public void Add(Socket client)
//        {
//            Console.WriteLine("{0} connected.", client.RemoteEndPoint);
//            var r = new ClientRequest(client);
//            if (r.QueryString != null && r.QueryString.ToLower() == "/a.jpg")
//            {
//                client.Send(_header);
//                lock (this)
//                {
//                    _clients.Add(client);
//                }
//                return;
//            }
//            client.Send(_error);
//            client.Shutdown(SocketShutdown.Both);
//            client.Close(1000);
//            throw new Exception("invalid request!");
//        }
//        protected override void OnTime(RawEventArgs e)
//        {
//            base.OnTime(e);
//            foreach (var c in _clients.ToArray())
//            {
//                try
//                {
//                    c.Send(_imageHeader);
//                    c.Send(e.Raw);
//                    c.Send(_imageFooter);
//                }
//                catch (Exception ex)
//                {
//                    Console.WriteLine("{0}: {1}", c.RemoteEndPoint, ex.Message);
//                    lock (this)
//                    {
//                        _clients.Remove(c);
//                    }
//                }
//            }
//        }
//    }
//    public class ImageClockClient : ImageClock
//    {
//        private readonly byte[] _header = Encoding.UTF8.GetBytes(@"HTTP/1.0 200 OK
//Content-Type: multipart/x-mixed-replace; boundary=BALABUBABABU
//
//--BALABUBABABU
//");
//        private readonly byte[] _imageHeader = Encoding.UTF8.GetBytes(@"Content-Type: image/jpeg
//
//");
//        private readonly byte[] _imageFooter = Encoding.UTF8.GetBytes(@"
//--BALABUBABABU
//");
//        private readonly byte[] _error = Encoding.UTF8.GetBytes(@"HTTP/1.0 404 Not Found
//Content-Length: 0
//Content-Type: text/html
//
//");

//        public Socket Client { get; private set; }
//        public ImageClockClient(Socket client)
//        {
//            Client = client;
//        }
//        public override void Start()
//        {
//            Console.WriteLine("{0} connected.", Client.RemoteEndPoint);
//            var r = new ClientRequest(Client);
//            if (r.QueryString != null && r.QueryString.ToLower() == "/a.jpg")
//            {
//                Client.Send(_header);
//                base.Start();
//                return;
//            }
//            Client.Send(_error);
//            Client.Shutdown(SocketShutdown.Both);
//            Client.Close(1000);
//            throw new Exception("invalid request!");
//        }
//        protected override void OnTime(RawEventArgs e)
//        {
//            base.OnTime(e);
//            try
//            {
//                Client.Send(_imageHeader);
//                Client.Send(e.Raw);
//                Client.Send(_imageFooter);
//            }
//            catch (Exception ex)
//            {
//                Console.WriteLine("{0}: {1}", Client.RemoteEndPoint, ex.Message);
//                Stop();
//            }
//        }
//    }
    public class TcpServer2
    {
        public event EventHandler<SocketClientEventArgs> ClientAccepted;
        protected virtual void OnClientAccepted(SocketClientEventArgs e)
        {
            if (ClientAccepted != null) ClientAccepted(this, e);
        }

        private bool _keepRunning;
        private Socket _socket;
        public int Port { get; private set; }
        public bool IsRunning { get; private set; }
        
        public TcpServer2(int port)
        {
            Port = port;
        }
        public void Start()
        {
            if (IsRunning) return;
            IsRunning = true;
            _keepRunning = true;
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _socket.Bind(new IPEndPoint(IPAddress.Any, Port));
            _socket.Listen(50);
            _socket.BeginAccept(AcceptClient, _socket);
        }
        private void AcceptClient(IAsyncResult ar)
        {
            try
            {
                var s = ar.AsyncState as Socket;
                if (s == null) return;
                var client = s.EndAccept(ar);
                OnClientAccepted(new SocketClientEventArgs(client));
                if (!_keepRunning) return;
                s.BeginAccept(AcceptClient, s);
                Trace.WriteLine("BeginAcceptClient");
            }
            catch (Exception ex)
            {
                Trace.WriteLine("BeginAcceptClient: " + ex.Message);
            }
        }
        public void Stop()
        {
            _keepRunning = false;
            _socket.Close();
        } 
    }
    public class RawEventArgs : EventArgs
    {
        public byte[] Raw { get; private set; }
        public RawEventArgs(byte[] raw)
        {
            Raw = raw;
        }
    }
    public class ImageClock
    {
        public event EventHandler<RawEventArgs> Time;
        protected virtual void OnTime(RawEventArgs e)
        {
            if (Time != null) Time(this, e);
        }
        private List<object> _keepRunning;
        public virtual void Start()
        {
            _keepRunning = new List<object>{"1"};
            new Thread(Execute){IsBackground = true}.Start();
        }

        public void Execute()
        {
            using (var bmp = new Bitmap(140, 30))
            {
                var rect = new Rectangle(0, 0, 140, 30);
                var font = new Font("Arial", 16);
                var brush = new SolidBrush(Color.Yellow);
                using (var mem = new MemoryStream())
                {
                    using (var g = Graphics.FromImage(bmp))
                    {
                        while (_keepRunning.Count > 0)
                        {
                            var nextTime = DateTime.Now.AddMilliseconds(1000.0/30.0);
                            g.Clear(Color.Black);
                            g.DrawString(DateTime.Now.ToString("HH:mm:ss.fff"), font, brush, rect);
                            mem.SetLength(0);
                            bmp.Save(mem, ImageFormat.Jpeg);
                            var bytes = mem.ToArray();
                            OnTime(new RawEventArgs(bytes));
                            var sleep = nextTime.Subtract(DateTime.Now);
                            if (sleep > TimeSpan.Zero) Thread.Sleep(sleep);
                        }
                    }
                }
            }
        }

        public void Stop()
        {
            _keepRunning.Clear();
        }
    }
    public class ContentTypes : Dictionary<string, string>
    {
        public ContentTypes(string filePath)
        {
            if (!File.Exists(filePath)) return;
            using (var r = new StreamReader(filePath))
            {
                var line = r.ReadLine();
                while (line != null)
                {
                    var index = line.IndexOf(" = ");
                    if (index > -1) this[line.Substring(0, index)] = line.Substring(index + 3);
                    line = r.ReadLine();
                }
            }
        }
    }
    public interface IHttpHandler
    {
        void Serve(ClientContext context);
    }
    public class HttpFileHandler : IHttpHandler
    {
        public Dictionary<string, string> ContentTypes { get; private set; }
        public string Root { get; private set; }
        public HttpFileHandler(string root, Dictionary<string, string> contentTypes)
        {
            Root = root;
            ContentTypes = contentTypes;
        }
        public void Serve(ClientContext request)
        {
            Console.WriteLine("{0} {1} {2} {3}", request.Client.RemoteEndPoint, request.Method, request.QueryString, request.Protocol);
            var path = Root + request.QueryString;
            if (File.Exists(path))
                ResponseFile(new FileInfo(path), request);
            else
                ResponseFileNotFound(request);
        }
        private void ResponseFile(FileInfo file, ClientContext request)
        {
            var ext = file.Extension.ToLower();
            var contentType = ContentTypes.ContainsKey(ext) ? ContentTypes[ext] : "application/octet-stream";
            var header = @"HTTP/1.0 {0}
Content-Type: {1}
Content-Length: {2}

"
                .Replace("{0}", "200 OK")
                .Replace("{1}", contentType)
                .Replace("{2}", file.Length.ToString());
            request.Client.Send(Encoding.UTF8.GetBytes(header));
            var buffer = new byte[2048];
            using (var s = file.OpenRead())
            {
                var len = s.Read(buffer, 0, buffer.Length);
                while (len > 0)
                {
                    request.Client.Send(buffer, 0, len, SocketFlags.None);
                    len = s.Read(buffer, 0, buffer.Length);
                }
            }
        }
        private static void ResponseFileNotFound(ClientContext request)
        {
            const string header = @"HTTP/1.0 404 Not Found
Content-Type: text/plain; charset=UTF-8
Content-Length: 14

FILE NOT FOUND";
            var buffer = Encoding.UTF8.GetBytes(header);
            request.Client.Send(buffer);
        }
    }
    //[Obsolete]
    //public class Request
    //{
    //    private readonly List<byte> _tmp;
    //    private readonly Encoding _enc = Encoding.UTF8;
    //    private readonly Action<byte> _contentHandler;
    //    private Action<byte> _read;
    //    private Dictionary<string, string> _query;
    //    public Socket Client;

    //    public Dictionary<string, string> Header;
    //    public string Method { get; private set; }
    //    public string QueryString { get; private set; }
    //    public string Protocol { get; private set; }
    //    public Dictionary<string, string> Query
    //    {
    //        get
    //        {
    //            if (_query == null)
    //            {
    //                _query = new Dictionary<string, string>();
    //                var index = QueryString.IndexOf('?');
    //                if (index > -1)
    //                {
    //                    var keyValues = QueryString.Substring(index + 1).Split('&');
    //                    foreach (var i in keyValues)
    //                    {
    //                        index = i.IndexOf('=');
    //                        _query[i.Substring(0, index)] =
    //                            (index > -1)
    //                            ? i.Substring(index + 1)
    //                            : string.Empty;
    //                    }
    //                }
    //            }
    //            return _query;
    //        }
    //    }
        
    //    public Request(Socket client)
    //        : this(client, null)
    //    {}
    //    public Request(Socket client, Action<byte> contentHandler)
    //    {
    //        Client = client;
    //        _contentHandler = contentHandler;
    //        Header = new Dictionary<string, string>();
    //        _read = ReadFirstLine;
    //        _tmp = new List<byte>();
    //        ReadRequest();
    //    }
    //    private void ReadRequest()
    //    {
    //        var buffer = new byte[1];
    //        var len = Client.Receive(buffer, SocketFlags.None);
    //        while (len > 0)
    //        {
    //            for (var i = 0; i < len; i++)
    //            {
    //                _read(buffer[i]);
    //                if (_read == null) return;
    //            }
    //            len = Client.Receive(buffer, SocketFlags.None);
    //        }
    //    }
    //    private void ReadFirstLine(byte data)
    //    {
    //        if (data == '\r') return;
    //        if (data != '\n')
    //        {
    //            _tmp.Add(data);
    //            return;
    //        }
    //        var args = _enc.GetString(_tmp.ToArray()).Split(' ');
    //        if (args.Length == 3)
    //        {
    //            Method = args[0];
    //            QueryString = args[1];
    //            Protocol = args[2];
    //        }
    //        _read = ReadHeader;
    //        _tmp.Clear();
    //    }
    //    private void ReadHeader(byte data)
    //    {
    //        if (data == '\r') return;
    //        if (data != '\n')
    //        {
    //            _tmp.Add(data);
    //            return;
    //        }
    //        if (_tmp.Count < 1)
    //        {
    //            _read = _contentHandler;
    //            _tmp.Clear();
    //            return;
    //        }
    //        var kv = _enc.GetString(_tmp.ToArray());
    //        var index = kv.IndexOf(":");
    //        if (index > -1)
    //            Header[kv.Substring(0, index)] = kv.Substring(index + 1);
    //        else
    //            Header[kv] = null;
    //        _tmp.Clear();
    //    }
    //    public NetworkStream GetRequestStream()
    //    {
    //        return new NetworkStream(Client);
    //    }
    //}
    public class ClientContext
    {
        private readonly List<byte> _tmp;
        private Action<byte> _read;

        public Socket Client { get; private set; }
        public NetworkStream Stream { get; private set; }
        public string Method { get; private set; }
        public string QueryString { get; private set; }
        public string Protocol { get; private set; }
        public Dictionary<string, string> Header { get; private set; }

        public ClientContext(Socket client)
        {
            Client = client;
            Stream = new NetworkStream(client);
            Header = new Dictionary<string, string>();
            _tmp = new List<byte>();
            ReadRequest();
        }

        private void ReadRequest()
        {
            _read = ReadFirstLine;
            var buffer = new byte[1];
            var len = Stream.Read(buffer, 0, 1);
            while (len > 0)
            {
                _read(buffer[0]);
                if (_read == null) return;
                len = Stream.Read(buffer, 0, 1);
            }
        }
        private void ReadFirstLine(byte data)
        {
            if (data == '\r') return;
            if (data != '\n') { _tmp.Add(data); return; }
            var args = Encoding.UTF8.GetString(_tmp.ToArray()).Split(' ');
            if (args.Length == 3)
            {
                Method = args[0];
                QueryString = args[1];
                Protocol = args[2];
            }
            _read = ReadHeader;
            _tmp.Clear();
        }
        private void ReadHeader(byte data)
        {
            if (data == '\r') return;
            if (data != '\n') { _tmp.Add(data); return; }
            if (_tmp.Count < 1)
            {
                _read = null;
                _tmp.Clear();
                return;
            }
            var kv = Encoding.UTF8.GetString(_tmp.ToArray());
            var index = kv.IndexOf(":");
            if (index > -1)
                Header[kv.Substring(0, index)] = kv.Substring(index + 1);
            else
                Header[kv] = null;
            _tmp.Clear();
        }

    }

    public class HttpServer
    {
        private bool _keepRunning;
        public bool IsRunning { get; private set; }
        public int Port { get; private set; }
        public IHttpHandler Handler { get; private set; }
        public HttpServer(int port, IHttpHandler handler)
        {
            Port = port;
            Handler = handler;
        }
        public void Start()
        {
            if (IsRunning) return;
            IsRunning = true;
            _keepRunning = true;
            try
            {
                var tcp = new TcpListener(IPAddress.Any, Port);
                tcp.Start();
                tcp.BeginAcceptSocket(AcceptClient, tcp);
            }
            catch (Exception)
            {
                IsRunning = false;
                throw;
            }
        }
        private void AcceptClient(IAsyncResult ar)
        {
            var tcp = ar.AsyncState as TcpListener;
            if (tcp == null)
            {
                IsRunning = false;
                return;
            }
            var client = tcp.EndAcceptSocket(ar);
            if (_keepRunning)
                tcp.BeginAcceptTcpClient(AcceptClient, tcp);
            else
                IsRunning = false;
            if (Handler == null) return;
            try
            {
                var context = new ClientContext(client);
                Handler.Serve(context);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        public void Stop()
        {
            _keepRunning = false;
        }
    }
}
